-
-
Notifications
You must be signed in to change notification settings - Fork 425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove unnecessary use of dynamics #652
Remove unnecessary use of dynamics #652
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basically when the response isn't used you don't need to specify the type (<object>
) just use the JObject overload
"document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'"); | ||
} | ||
|
||
using (var browser2 = await Puppeteer.LaunchAsync(options, TestConstants.LoggerFactory)) | ||
{ | ||
var page2 = await browser2.NewPageAsync(); | ||
await page2.GoToAsync(TestConstants.EmptyPage); | ||
Assert.Equal("doSomethingOnlyOnce=true", await page2.EvaluateExpressionAsync("document.cookie")); | ||
Assert.Equal("doSomethingOnlyOnce=true", await page2.EvaluateExpressionAsync<object>("document.cookie")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should probably be EvaluateExpressionAsync<string>
@@ -156,15 +156,15 @@ public async Task UserDataDirOptionShouldRestoreCookies() | |||
{ | |||
var page = await browser.NewPageAsync(); | |||
await page.GoToAsync(TestConstants.EmptyPage); | |||
await page.EvaluateExpressionAsync( | |||
await page.EvaluateExpressionAsync<object>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this use the JObject overload without specifying <object>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned in the PR description, I thought by removing the default object and dynamic methods we would make users think about what they want, I can add back methods defaulting to object though ?
} | ||
|
||
using (var browser2 = await Puppeteer.LaunchAsync(options, TestConstants.LoggerFactory)) | ||
{ | ||
var page2 = await browser2.NewPageAsync(); | ||
await page2.GoToAsync(TestConstants.EmptyPage); | ||
Assert.Equal("hello", await page2.EvaluateExpressionAsync("localStorage.hey")); | ||
Assert.Equal("hello", await page2.EvaluateExpressionAsync<object>("localStorage.hey")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be <string>
not <object>
@@ -402,7 +402,7 @@ public async Task ShouldSelectTheTextWithMouse() | |||
await Page.FocusAsync("textarea"); | |||
const string text = "This is the text that we are going to try to select. Let's see how it goes."; | |||
await Page.Keyboard.TypeAsync(text); | |||
await Page.EvaluateExpressionAsync("document.querySelector('textarea').scrollTop = 0"); | |||
await Page.EvaluateExpressionAsync<object>("document.querySelector('textarea').scrollTop = 0"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this use the JObject overload without specifying <object>
?
@@ -448,7 +448,7 @@ public async Task ShouldFireContextmenuEventOnRightClick() | |||
public async Task ShouldSetModifierKeysOnClick() | |||
{ | |||
await Page.GoToAsync(TestConstants.ServerUrl + "/input/scrollable.html"); | |||
await Page.EvaluateExpressionAsync("document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true)"); | |||
await Page.EvaluateExpressionAsync<object>("document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true)"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this use the JObject overload without specifying <object>
?
@@ -476,7 +476,7 @@ public async Task ShouldSpecifyRepeatProperty() | |||
{ | |||
await Page.GoToAsync(TestConstants.ServerUrl + "/input/textarea.html"); | |||
await Page.FocusAsync("textarea"); | |||
await Page.EvaluateExpressionAsync("document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true)"); | |||
await Page.EvaluateExpressionAsync<object>("document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true)"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this use the JObject overload without specifying <object>
?
@@ -505,7 +505,7 @@ public async Task ShouldClickLinksWhichCauseNavigation() | |||
public async Task ShouldTweenMouseMovement() | |||
{ | |||
await Page.Mouse.MoveAsync(100, 100); | |||
await Page.EvaluateExpressionAsync(@"{ | |||
await Page.EvaluateExpressionAsync<object>(@"{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this use the JObject overload without specifying <object>
?
@@ -580,7 +580,7 @@ public async Task ShouldTypeAllKindsOfCharacters() | |||
public async Task ShouldSpecifyLocation() | |||
{ | |||
await Page.GoToAsync(TestConstants.ServerUrl + "/input/textarea.html"); | |||
await Page.EvaluateExpressionAsync(@"{ | |||
await Page.EvaluateExpressionAsync<object>(@"{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this use the JObject overload without specifying <object>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make this change first so we get an smaller PR to review.
@@ -17,7 +17,7 @@ public static async Task Main(string[] args) | |||
using (var browser = await Puppeteer.LaunchAsync(options)) | |||
using (var page = await browser.NewPageAsync()) | |||
{ | |||
await page.EvaluateFunctionAsync("_dumpioTextToLog => console.log(_dumpioTextToLog)", args[0]); | |||
await page.EvaluateFunctionAsync<object>("_dumpioTextToLog => console.log(_dumpioTextToLog)", args[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to support "void" functions. That function won't return a value. I don't think we have to force the user to pick a Type he won't need.
This comment applies to many changes here. If we don't need a result, we don't need a generic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should return Task<JToken>
/// <summary> | ||
/// Executes a script in browser context | ||
/// </summary> | ||
/// <param name="script">Script to be evaluated in browser context</param> | ||
/// <remarks> | ||
/// If the script, returns a Promise, then the method would wait for the promise to resolve and return its value. | ||
/// </remarks> | ||
/// <seealso cref="EvaluateFunctionAsync(string, object[])"/> | ||
/// <seealso cref="EvaluateFunctionAsync{T}(string, object[])"/> | ||
/// <seealso cref="EvaluateExpressionHandleAsync(string)"/> | ||
/// <returns>Task which resolves to script return value</returns> | ||
public Task<object> EvaluateExpressionAsync(string script) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should return Task<JToken>
@tommymonk there are still some tests that need to be reverted. On the other hand, this PR is conflicted. Could you resolve the conflicts? So the CI works on this PR? BTW, thanks for the calories burned on this PR! |
I'm almost finished on the overloads the the Evaluate* methods but I've got test failures because the JToken now returned can 'empty' rather than null as confirmed by it's HasValues property. Would you rather I check for the HasValues property and return null, or task the calling code (tests and users) with checking this property ? |
@tommymonk now the We could also play smart and check if the user wants the raw JSON token |
Unfortuantely I can't have overloads of these methods that return nothing (Task) as well as |
@kblok rebased atop your commits today |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, Getting rid of dynamics was something we always wanted to do and it's quite a big change but it worth the effort.
@@ -89,7 +89,8 @@ public async Task ShouldClickWrappedLinks() | |||
public async Task ShouldClickOnCheckboxInputAndToggle() | |||
{ | |||
await Page.GoToAsync(TestConstants.ServerUrl + "/input/checkbox.html"); | |||
Assert.Null(await Page.EvaluateExpressionAsync("result.check")); | |||
var foo = await Page.EvaluateExpressionAsync("result.check"); | |||
Assert.Null(foo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's undo this change we don't need it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to take it out when debugging
lib/PuppeteerSharp/BoundingBox.cs
Outdated
@@ -13,25 +13,25 @@ public class BoundingBox : IEquatable<BoundingBox> | |||
/// The x coordinate of the element in pixels. | |||
/// </summary> | |||
/// <value>The x.</value> | |||
[JsonProperty("x")] | |||
[JsonProperty(Constants.X)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep JsonProperties as strings it doesn't add much value to use constants here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're suggesting undoing all the work I put into removing strings scattered throughout the code ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sorry about that. It's not something I think we need to do, and it's out of the scope of removing dynamics.
Is there a reason that you don't just use BaristaLabs to generate all your poco objects for each of the devtools APIs, seems like this would take care of Response types you're wanting ? |
@tommymonk, basically we were growing little by little not even knowing that this project would get any interest. We started with dynamics, and then we began moving to classes, adding small classes as we needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good
@@ -72,7 +73,7 @@ public async Task ShouldBeAbleToDetachSession() | |||
var client = await Page.Target.CreateCDPSessionAsync(); | |||
await client.SendAsync("Runtime.enable"); | |||
var evalResponse = await client.SendAsync("Runtime.evaluate", new { expression = "1 + 2", returnByValue = true }); | |||
Assert.Equal(3, evalResponse.result.value.ToObject<int>()); | |||
Assert.Equal(3, evalResponse["result"]["value"].ToObject<int>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thinking about an example. Would a user be able to do this?
dynamic evalResponse = await client.SendAsync<ExpandoObject>("Runtime.evaluate", new { expression = "1 + 2", returnByValue = true });
Assert.Equal(3, evalResponse.result.value.ToObject<int>());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even easier;
dynamic evalResponse = await client.SendAsync("Runtime.evaluate", new { expression = "1 + 2", returnByValue = true });
Assert.Equal(3, evalResponse.result.value.ToObject<int>());
Or
var evalResponse = await client.SendAsync("Runtime.evaluate", new { expression = "1 + 2", returnByValue = true });
Assert.Equal(3, evalResponse.SelectToken("result.value"));
lib/PuppeteerSharp/IConnection.cs
Outdated
@@ -1,30 +1,31 @@ | |||
using System.Collections.Generic; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
@@ -0,0 +1,21 @@ | |||
using Newtonsoft.Json.Linq; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this to the Helper folder
@@ -1,39 +1,39 @@ | |||
using System.Collections.Generic; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's reduce the changelog here
@@ -1,16 +1,16 @@ | |||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's reduce the changelog
…tests that were not passing the correct <T>
Removed line-ending changes to the files mentioned, merged in your master changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this progress. Great job!
lib/PuppeteerSharp.Tests/PageTests/SetRequestInterceptionTests.cs
Outdated
Show resolved
Hide resolved
Just wanted to confirm you aren't waiting on me for any further rework ? |
@tommymonk cool I didn't know it was ready again. |
Besides my @tommymonk would you mind writing a Breaking Change report so we tell the users what it's changing for them and what they would need to change to get their app working? |
@Meir017 let's try to merge this before any other PR, so we don't conflict our friend @tommymonk |
I'm bound to have forgotten something, but will update this comment with your feedback. Should we mention the change of Evaluate* return type changing from dynamic to JToken because the current type being boxed as dynamic is JToken, though ? Use of evaluative methods that previously resolved to a JavaScript |
@tommymonk think about the code that a user might need to change, so we add that to our release notes. |
Thanks |
I believe that I've found a bug introduced by my PR that isn't covered by test.
The return type of EvaluateExpressionAsync isn't specified, the object is string and it return null Its cause because of this line of code in ExecutionContext; Instead of just checking
|
@tommymonk go for it an add a test :) |
My branch is gone now so I'll create a new one and new PR ? |
@tommymonk yes |
I'll get on it, can you suggest where is appropriate fore new tests to go ? |
@tommymonk I would go with PageTests.EvaluateTests.ShouldWorkWithoutGenerics() |
The use of dynamic throughout the code made it difficult to safely make a change relating to #650
Dynamics are terrible for hiding refactor mistakes until run-time.
I've plumbed through JObject for response and JToken for method interface, this clarity allowed me to do a lot of streamlining of the response processing and in some places identify where we are serialize/de-serialize the same data many times.
I didn't want to add to the string debt so I pulled out a large amount of the key strings into a Consts class to help with quality and refactor and reduce risk of future mistake.
As discussed in #650 it seemed like a sensible idea to remove the 'dynamic' or 'object overloads of methods like EvaluateFunctionAsync which are a cause of confusion, however if someone wants to use the type 'object' they will now get the correct types serialized for numerics.
All of the existing tests are passing for me, I wanted to add extra tests to cover #650 but wasn't sure which test class to use, maybe you could guide me ?